﻿#pragma warning disable 3001
#pragma warning disable 3002
#pragma warning disable 3003
// ***********************************************************************
// Assembly         : TSharp.Core
// Author           : admin
// Created          : 08-29-2013
//
// Last Modified By : admin
// Last Modified On : 08-30-2013
// ***********************************************************************
// <copyright file="MD5Util.cs" company="">
//     Copyright (c) . All rights reserved.
// </copyright>
// <summary></summary>
// ***********************************************************************
using System;
using System.Security.Cryptography;

namespace TSharp.Core.Security
{
    //end class: HashHelper
    /// <summary>
    ///     提供 CRC32 算法的实现
    /// </summary>
    public class Crc32 : HashAlgorithm
    {
        /// <summary>
        ///     The default polynomial
        /// </summary>
        public const UInt32 DefaultPolynomial = 0xedb88320;

        /// <summary>
        ///     The default seed
        /// </summary>
        public const UInt32 DefaultSeed = 0xffffffff;

        /// <summary>
        ///     The default table
        /// </summary>
        private static UInt32[] _defaultTable;

        /// <summary>
        ///     The seed
        /// </summary>
        private readonly UInt32 _seed;

        /// <summary>
        ///     The table
        /// </summary>
        private readonly UInt32[] _table;

        /// <summary>
        ///     The hash
        /// </summary>
        private UInt32 _hash;

        /// <summary>
        ///     Initializes a new instance of the <see cref="Crc32" /> class.
        /// </summary>
        public Crc32()
        {
            _table = InitializeTable(DefaultPolynomial);
            _seed = DefaultSeed;
            Initialize();
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="Crc32" /> class.
        /// </summary>
        /// <param name="polynomial">The polynomial.</param>
        /// <param name="seed">The seed.</param>
        public Crc32(UInt32 polynomial, UInt32 seed)
        {
            _table = InitializeTable(polynomial);
            _seed = seed;
            Initialize();
        }

        /// <summary>
        ///     初始化 <see cref="T:System.Security.Cryptography.HashAlgorithm" /> 类的实现。
        /// </summary>
        public override sealed void Initialize()
        {
            _hash = _seed;
        }

        /// <summary>
        ///     Hashes the core.
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        /// <param name="start">The start.</param>
        /// <param name="length">The length.</param>
        protected override void HashCore(byte[] buffer, int start, int length)
        {
            _hash = CalculateHash(_table, _hash, buffer, start, length);
        }

        /// <summary>
        ///     当在派生类中重写时，在加密流对象处理完最后的数据后完成哈希计算。
        /// </summary>
        /// <returns>计算所得的哈希代码。</returns>
        protected override byte[] HashFinal()
        {
            byte[] hashBuffer = UInt32ToBigEndianBytes(~_hash);
            HashValue = hashBuffer;
            return hashBuffer;
        }

        /// <summary>
        ///     Computes the specified buffer.
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        /// <returns>UInt32.</returns>
        public static UInt32 Compute(byte[] buffer)
        {
            return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length);
        }

        /// <summary>
        ///     Computes the specified seed.
        /// </summary>
        /// <param name="seed">The seed.</param>
        /// <param name="buffer">The buffer.</param>
        /// <returns>UInt32.</returns>
        public static UInt32 Compute(UInt32 seed, byte[] buffer)
        {
            return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length);
        }

        /// <summary>
        ///     Computes the specified polynomial.
        /// </summary>
        /// <param name="polynomial">The polynomial.</param>
        /// <param name="seed">The seed.</param>
        /// <param name="buffer">The buffer.</param>
        /// <returns>UInt32.</returns>
        public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
        {
            return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
        }

        /// <summary>
        ///     Initializes the table.
        /// </summary>
        /// <param name="polynomial">The polynomial.</param>
        /// <returns>UInt32[][].</returns>
        private static UInt32[] InitializeTable(UInt32 polynomial)
        {
            if (polynomial == DefaultPolynomial && _defaultTable != null)
            {
                return _defaultTable;
            }
            var createTable = new UInt32[256];
            for (int i = 0; i < 256; i++)
            {
                var entry = (UInt32)i;
                for (int j = 0; j < 8; j++)
                {
                    if ((entry & 1) == 1)
                        entry = (entry >> 1) ^ polynomial;
                    else
                        entry = entry >> 1;
                }
                createTable[i] = entry;
            }
            if (polynomial == DefaultPolynomial)
            {
                _defaultTable = createTable;
            }
            return createTable;
        }

        /// <summary>
        ///     Calculates the hash.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <param name="seed">The seed.</param>
        /// <param name="buffer">The buffer.</param>
        /// <param name="start">The start.</param>
        /// <param name="size">The size.</param>
        /// <returns>UInt32.</returns>
        private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size)
        {
            UInt32 crc = seed;
            for (int i = start; i < size; i++)
            {
                unchecked
                {
                    crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
                }
            }
            return crc;
        }

        /// <summary>
        ///     Us the int32 to big endian bytes.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <returns>System.Byte[][].</returns>
        private byte[] UInt32ToBigEndianBytes(UInt32 x)
        {
            return new[] { (byte)((x >> 24) & 0xff), (byte)((x >> 16) & 0xff), (byte)((x >> 8) & 0xff), (byte)(x & 0xff) };
        }
    }

    //end class: Crc32
}